home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / htfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  28.7 KB  |  1,111 lines

  1. /*            File Access                HTFile.c
  2. **            ===========
  3. **
  4. **    This is unix-specific code in general, with some VMS bits.
  5. **    These are routines for file access used by browsers.
  6. **
  7. ** History:
  8. **       Feb 91    Written Tim Berners-Lee CERN/CN
  9. **       Apr 91    vms-vms access included using DECnet syntax
  10. **    26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
  11. **            Fixed access bug for relative names on VMS.
  12. **       Sep 93 (MD)  Access to VMS files allows sharing.
  13. **    15 Nov 93 (MD)    Moved HTVMSname to HTVMSUTILS.C
  14. **
  15. ** Bugs:
  16. **    FTP: Cannot access VMS files from a unix machine.
  17. **      How can we know that the
  18. **    target machine runs VMS?
  19. */
  20.  
  21. #include"capalloc.h"
  22. #include "HTFile.h"        /* Implemented here */
  23. #include"capstdio.h"
  24.  
  25.  
  26. #define INFINITY 512        /* file name length @@ FIXME */
  27. #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
  28.  
  29. #include "HTUtils.h"
  30.  
  31. #ifdef VMS
  32. #include "HTVMSUtils.h"
  33. #endif /* VMS */
  34.  
  35. #include "HTParse.h"
  36. #include "tcp.h"
  37. #include "HTTCP.h"
  38. #ifndef DECNET
  39. #include "HTFTP.h"
  40. #endif
  41. #include "HTAnchor.h"
  42. #include "HTAtom.h"
  43. #include "HTWriter.h"
  44. #include "HTFWriter.h"
  45. #include "HTInit.h"
  46. #include "HTBTree.h"
  47.  
  48. typedef struct _HTSuffix {
  49.     char *        suffix;
  50.     HTAtom *    rep;
  51.     HTAtom *    encoding;
  52.     float        quality;
  53. } HTSuffix;
  54.  
  55.  
  56. #ifdef USE_DIRENT        /* Set this for Sys V systems */
  57. #define STRUCT_DIRENT struct dirent
  58. #else
  59. #define STRUCT_DIRENT struct direct
  60. #endif
  61.  
  62. #include "HTML.h"        /* For directory object building */
  63.  
  64. #define PUTC(c) (*target->isa->put_character)(target, c)
  65. #define PUTS(s) (*target->isa->put_string)(target, s)
  66. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  67. #define END(e) (*target->isa->end_element)(target, e)
  68. #define FREE_TARGET (*target->isa->free)(target)
  69. struct _HTStructured {
  70.     CONST HTStructuredClass *    isa;
  71.     /* ... */
  72. };
  73.  
  74.  
  75. /*                   Controlling globals
  76. **
  77. */
  78.  
  79. PUBLIC int HTDirAccess = HT_DIR_OK;
  80. PUBLIC int HTDirReadme = HT_DIR_README_TOP;
  81.  
  82. PRIVATE char *HTMountRoot = "/Net/";        /* Where to find mounts */
  83. #ifdef VMS
  84. PRIVATE char *HTCacheRoot = "/WWW$SCRATCH";   /* Where to cache things */
  85. #else
  86. PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
  87. #endif
  88.  
  89. /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
  90.  
  91.  
  92. /*    Suffix registration
  93. */
  94.  
  95. PRIVATE HTList * HTSuffixes = 0;
  96. PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
  97. PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
  98.  
  99.  
  100. /*    Define the representation associated with a file suffix
  101. **    -------------------------------------------------------
  102. **
  103. **    Calling this with suffix set to "*" will set the default
  104. **    representation.
  105. **    Calling this with suffix set to "*.*" will set the default
  106. **    representation for unknown suffix files which contain a ".".
  107. **
  108. **    If filename suffix is already defined its previous
  109. **    definition is overridden.
  110. */
  111. PUBLIC void HTSetSuffix ARGS4(
  112.     CONST char *,    suffix,
  113.     CONST char *,    representation,
  114.     CONST char *,    encoding,
  115.     float,        value)
  116. {
  117.     
  118.     HTSuffix * suff;
  119.  
  120.     if (strcmp(suffix, "*")==0) suff = &no_suffix;
  121.     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
  122.     else {
  123.     HTList *cur = HTSuffixes;
  124.  
  125.     while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
  126.         if (suff->suffix && 0==strcmp(suff->suffix, suffix))
  127.         break;
  128.     }
  129.     if (!suff) { /* Not found -- create a new node */
  130.         suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
  131.         if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
  132.     
  133.         if (!HTSuffixes) HTSuffixes = HTList_new();
  134.         HTList_addObject(HTSuffixes, suff);
  135.     
  136.         StrAllocCopy(suff->suffix, suffix);
  137.     }
  138.     }
  139.  
  140.     suff->rep = HTAtom_for(representation);
  141.  
  142.     {
  143.         char * enc = NULL;
  144.     char * p;
  145.     StrAllocCopy(enc, encoding);
  146.     for (p=enc; *p; p++) *p = TOLOWER(*p);
  147.     suff->encoding = HTAtom_for(encoding);
  148.     }
  149.     
  150.     suff->quality = value;
  151. }
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159. /*    Send README file
  160. **
  161. **  If a README file exists, then it is inserted into the document here.
  162. */
  163.  
  164. #ifdef GOT_READ_DIR
  165. PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
  166.     FILE * fp;
  167.     char * readme_file_name = 
  168.     malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
  169.     strcpy(readme_file_name, localname);
  170.     strcat(readme_file_name, "/");
  171.     strcat(readme_file_name, HT_DIR_README_FILE);
  172.     
  173.     fp = fopen(readme_file_name,  "r");
  174.     
  175.     if (fp) {
  176.     HTStructuredClass targetClass;
  177.     
  178.     targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
  179. #ifdef MSDOSMEM
  180.     {
  181.         extern void *vp_msdosmem;
  182.         extern void **vpp_msdosmem;
  183.         vp_msdosmem = NULL;
  184.         vpp_msdosmem = NULL;
  185.     }
  186. #endif /* MSDOSMEM */
  187.     START(HTML_PRE);
  188.     for(;;){
  189.         char c = fgetc(fp);
  190.         if (c == (char)EOF) break;
  191.         switch (c) {
  192.             case '&':
  193.         case '<':
  194.         case '>':
  195.             PUTC('&');
  196.             PUTC('#');
  197.             PUTC((char)(c / 10));
  198.             PUTC((char) (c % 10));
  199.             PUTC(';');
  200.             break;
  201. /*            case '\n':
  202.             PUTC('\r');    
  203. Bug removed thanks to joe@athena.mit.edu */            
  204.         default:
  205.             PUTC(c);
  206.         }
  207.     }
  208.     END(HTML_PRE);
  209.     fclose(fp);
  210.     } 
  211. }
  212. #endif
  213.  
  214.  
  215. /*    Make the cache file name for a W3 document
  216. **    ------------------------------------------
  217. **    Make up a suitable name for saving the node in
  218. **
  219. **    E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
  220. **        /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
  221. **
  222. ** On exit,
  223. **    returns    a malloc'ed string which must be freed by the caller.
  224. */
  225. PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
  226. {
  227.     char * access = HTParse(name, "", PARSE_ACCESS);
  228.     char * host = HTParse(name, "", PARSE_HOST);
  229.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  230.     
  231.     char * result;
  232.     result = (char *)malloc(
  233.         strlen(HTCacheRoot)+strlen(access)
  234.         +strlen(host)+strlen(path)+6+1);
  235.     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
  236.     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
  237.     free(path);
  238.     free(access);
  239.     free(host);
  240.     return result;
  241. }
  242.  
  243.  
  244. /*    Open a file for write, creating the path
  245. **    ----------------------------------------
  246. */
  247. #ifdef NOT_IMPLEMENTED
  248. PRIVATE int HTCreatePath ARGS1(CONST char *,path)
  249. {
  250.     return -1;
  251. }
  252. #endif
  253.  
  254. /*    Convert filenames between local and WWW formats
  255. **    -----------------------------------------------
  256. **    Make up a suitable name for saving the node in
  257. **
  258. **    E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
  259. **        $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
  260. **
  261. ** On exit,
  262. **    returns    a malloc'ed string which must be freed by the caller.
  263. */
  264. PUBLIC char * HTLocalName ARGS1(CONST char *,name)
  265. {
  266.     char * access = HTParse(name, "", PARSE_ACCESS);
  267.     char * host = HTParse(name, "", PARSE_HOST);
  268.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  269.     
  270.     HTUnEscape(path);    /* Interpret % signs */
  271.     
  272.     if (0==strcmp(access, "file")) { /* local file */
  273.         free(access);    
  274.     if ((0==strcasecomp(host, HTHostName())) ||
  275.         (0==strcasecomp(host, "localhost")) || !*host) {
  276.         free(host);
  277.         if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
  278. #ifdef MSDOS
  279.         {
  280.             /*
  281.              *    Convert the path to a dos file name.
  282.              */
  283.             extern void urltodos(char *);
  284.  
  285.             urltodos(path);
  286.         }
  287. #endif /* MSDOS */
  288.         return(path);
  289.     } else {
  290.         char * result = (char *)malloc(
  291.                     strlen("/Net/")+strlen(host)+strlen(path)+1);
  292.           if (result == NULL) outofmem(__FILE__, "HTLocalName");
  293.         sprintf(result, "%s%s%s", "/Net/", host, path);
  294.         free(host);
  295.         free(path);
  296.         if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
  297.         return result;
  298.     }
  299.     } else {  /* other access */
  300.     char * result;
  301.         CONST char * home =  (CONST char*)getenv("HOME");
  302. #ifdef VMS
  303.     if (!home) 
  304.         home = HTCacheRoot; 
  305.     else
  306.            home = HTVMS_wwwName(home);
  307. #else /* not VMS */
  308.     if (!home) home = "/tmp"; 
  309. #endif /* not VMS */
  310.     result = (char *)malloc(
  311.         strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
  312.       if (result == NULL) outofmem(__FILE__, "HTLocalName");
  313.     sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
  314.     free(path);
  315.     free(access);
  316.     free(host);
  317.     return result;
  318.     }
  319. }
  320.  
  321.  
  322. /*    Make a WWW name from a full local path name
  323. **
  324. ** Bugs:
  325. **    At present, only the names of two network root nodes are hand-coded
  326. **    in and valid for the NeXT only. This should be configurable in
  327. **    the general case.
  328. */
  329.  
  330. PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
  331. {
  332.     char * result;
  333. #ifdef NeXT
  334.     if (0==strncmp("/private/Net/", name, 13)) {
  335.     result = (char *)malloc(7+strlen(name+13)+1);
  336.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  337.     sprintf(result, "file://%s", name+13);
  338.     } else
  339. #endif
  340.     if (0==strncmp(HTMountRoot, name, 5)) {
  341.     result = (char *)malloc(7+strlen(name+5)+1);
  342.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  343.     sprintf(result, "file://%s", name+5);
  344.     } else {
  345.         result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
  346.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  347.     sprintf(result, "file://%s%s", HTHostName(), name);
  348.     }
  349.     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
  350.     return result;
  351. }
  352.  
  353.  
  354. /*    Determine a suitable suffix, given the representation
  355. **    -----------------------------------------------------
  356. **
  357. ** On entry,
  358. **    rep    is the atomized MIME style representation
  359. **
  360. ** On exit,
  361. **    returns    a pointer to a suitable suffix string if one has been
  362. **        found, else "".
  363. */
  364. PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
  365. {
  366.     HTSuffix * suff;
  367.     int n;
  368.     int i;
  369.  
  370. #ifndef NO_INIT    
  371.     if (!HTSuffixes) HTFileInit();
  372. #endif
  373.     n = HTList_count(HTSuffixes);
  374.     for(i=0; i<n; i++) {
  375.     suff = HTList_objectAt(HTSuffixes, i);
  376.     if (suff->rep == rep) {
  377.         return suff->suffix;        /* OK -- found */
  378.     }
  379.     }
  380.     return "";        /* Dunno */
  381. }
  382.  
  383.  
  384. /*    Determine file format from file name
  385. **    ------------------------------------
  386. **
  387. **    This version will return the representation and also set
  388. **    a variable for the encoding.
  389. **
  390. **    It will handle for example  x.txt, x.txt,Z, x.Z
  391. */
  392.  
  393. PUBLIC HTFormat HTFileFormat ARGS2 (
  394.             CONST char *,    filename,
  395.             HTAtom **,    pencoding)
  396.  
  397. {
  398.     HTSuffix * suff;
  399.     int n;
  400.     int i;
  401.     int lf = strlen(filename);
  402.  
  403. #ifndef NO_INIT    
  404.     if (!HTSuffixes) HTFileInit();
  405. #endif
  406.     *pencoding = NULL;
  407.     n = HTList_count(HTSuffixes);
  408.     for(i=0; i<n; i++) {
  409.         int ls;
  410.     suff = HTList_objectAt(HTSuffixes, i);
  411.     ls = strlen(suff->suffix);
  412.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  413.         int j;
  414.         *pencoding = suff->encoding;
  415.         if (suff->rep) return suff->rep;        /* OK -- found */
  416.         
  417.         for(j=0; j<n; j++) {  /* Got encoding, need representation */
  418.         int ls2;
  419.         suff = HTList_objectAt(HTSuffixes, j);
  420.         ls2 = strlen(suff->suffix);
  421.         if ((ls <= lf) && 0==strncmp(
  422.             suff->suffix, filename + lf - ls -ls2, ls2)) {
  423.             if (suff->rep) return suff->rep;
  424.         }
  425.         }
  426.         
  427.     }
  428.     }
  429.     
  430.     /* defaults tree */
  431.     
  432.     suff = strchr(filename, '.') ?     /* Unknown suffix */
  433.      ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
  434.      : &no_suffix;
  435.      
  436.     /* set default encoding unless found with suffix already */
  437.     if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
  438.                     : HTAtom_for("binary");
  439.     return suff->rep ? suff->rep : WWW_BINARY;
  440. }
  441.  
  442.  
  443. /*    Determine value from file name
  444. **    ------------------------------
  445. **
  446. */
  447.  
  448. PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
  449.  
  450. {
  451.     HTSuffix * suff;
  452.     int n;
  453.     int i;
  454.     int lf = strlen(filename);
  455.  
  456. #ifndef NO_INIT    
  457.     if (!HTSuffixes) HTFileInit();
  458. #endif
  459.     n = HTList_count(HTSuffixes);
  460.     for(i=0; i<n; i++) {
  461.         int ls;
  462.     suff = HTList_objectAt(HTSuffixes, i);
  463.     ls = strlen(suff->suffix);
  464.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  465.         if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
  466.                    filename, suff->quality);
  467.         return suff->quality;        /* OK -- found */
  468.     }
  469.     }
  470.     return 0.3;        /* Dunno! */
  471. }
  472.  
  473.  
  474. /*    Determine write access to a file
  475. **    --------------------------------
  476. **
  477. ** On exit,
  478. **    return value    YES if file can be accessed and can be written to.
  479. **
  480. ** Bugs:
  481. **    1.    No code for non-unix systems.
  482. **    2.    Isn't there a quicker way?
  483. */
  484.  
  485. #ifdef VMS
  486. #define NO_GROUPS
  487. #endif
  488. #ifdef NO_UNIX_IO
  489. #define NO_GROUPS
  490. #endif
  491. #ifdef PCNFS
  492. #define NO_GROUPS
  493. #endif
  494. #ifdef MSDOS
  495. #define NO_GROUPS
  496. #endif
  497.  
  498. PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
  499. {
  500. #ifdef NO_GROUPS
  501.     return NO;        /* Safe answer till we find the correct algorithm */
  502. #else
  503.     int     groups[NGROUPS];
  504.     uid_t    myUid;
  505.     int        ngroups;            /* The number of groups  */
  506.     struct stat    fileStatus;
  507.     int        i;
  508.  
  509.     if (stat(filename, &fileStatus))        /* Get details of filename */
  510.     return NO;                /* Can't even access file! */
  511.  
  512.     ngroups = getgroups(NGROUPS, groups);    /* Groups to which I belong  */
  513.     myUid = geteuid();                /* Get my user identifier */
  514.  
  515.     if (TRACE) {
  516.         int i;
  517.     fprintf(stderr, 
  518.         "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  519.             (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
  520.         fileStatus.st_gid,
  521.         myUid, ngroups);
  522.     for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
  523.     fprintf(stderr, ")\n");
  524.     }
  525.     
  526.     if (fileStatus.st_mode & 0002)        /* I can write anyway? */
  527.         return YES;
  528.     
  529.     if ((fileStatus.st_mode & 0200)        /* I can write my own file? */
  530.      && (fileStatus.st_uid == myUid))
  531.         return YES;
  532.  
  533.     if (fileStatus.st_mode & 0020)        /* Group I am in can write? */
  534.     {
  535.        for (i=0; i<ngroups; i++) {
  536.             if (groups[i] == fileStatus.st_gid)
  537.         return YES;
  538.     }
  539.     }
  540.     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
  541.     return NO;                    /* If no excuse, can't do */
  542. #endif
  543. }
  544.  
  545.  
  546. /*    Make a save stream
  547. **    ------------------
  548. **
  549. **    The stream must be used for writing back the file.
  550. **    @@@ no backup done
  551. */
  552. PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
  553. {
  554.  
  555.     CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
  556.     char *  localname = HTLocalName(addr);
  557.  
  558.     FILE* fp = fopen(localname, "w");
  559.     if (!fp) return NULL;
  560.     
  561.     return HTFWriter_new(fp);
  562.     
  563. }
  564.  
  565. /*      Output one directory entry
  566. **
  567. */
  568. PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
  569.          CONST char * , tail,
  570.          CONST char *,  entry)
  571. {
  572.     char * relative;
  573.     char * escaped = HTEscape(entry, URL_XPALPHAS);
  574.  
  575.     /* If empty tail, gives absolute ref below */
  576.     relative = (char*) malloc(
  577.                   strlen(tail) + strlen(escaped)+2);
  578.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  579.     sprintf(relative, "%s/%s", tail, escaped);
  580.     HTStartAnchor(target, NULL, relative);
  581.     free(escaped);
  582.     free(relative);
  583.     PUTS(entry);
  584.     END(HTML_A);
  585. }
  586.  
  587. /*      Output parent directory entry
  588. **
  589. **    This gives the TITLE and H1 header, and also a link
  590. **    to the parent directory if appropriate.
  591. */
  592. PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
  593.          HTAnchor * , anchor)
  594.  
  595. {
  596.     char * logical = HTAnchor_address(anchor);
  597.     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
  598.     char * current;
  599.  
  600.     current = strrchr(path, '/');    /* last part or "" */
  601.     free(logical);
  602.  
  603.     {
  604.       char * printable = NULL;
  605.       StrAllocCopy(printable, (current + 1));
  606.       HTUnEscape(printable);
  607. #ifdef MSDOSMEM
  608.     {
  609.         extern void *vp_msdosmem;
  610.         extern void **vpp_msdosmem;
  611.         vp_msdosmem = NULL;
  612.         vpp_msdosmem = NULL;
  613.     }
  614. #endif /* MSDOSMEM */
  615.       START(HTML_TITLE);
  616.       PUTS(*printable ? printable : "Welcome ");
  617.       PUTS(" directory");
  618.       END(HTML_TITLE);    
  619.     
  620. #ifdef MSDOSMEM
  621.     {
  622.         extern void *vp_msdosmem;
  623.         extern void **vpp_msdosmem;
  624.         vp_msdosmem = NULL;
  625.         vpp_msdosmem = NULL;
  626.     }
  627. #endif /* MSDOSMEM */
  628.       START(HTML_H1);
  629.       PUTS(*printable ? printable : "Welcome");
  630.       END(HTML_H1);
  631.       free(printable);
  632.     }
  633.  
  634.     /*  Make link back to parent directory
  635.      */
  636.  
  637.     if (current && current[1]) {   /* was a slash AND something else too */
  638.         char * parent;
  639.     char * relative;
  640.     *current++ = 0;
  641.       parent = strrchr(path, '/');  /* penultimate slash */
  642.  
  643.     relative = (char*) malloc(strlen(current) + 4);
  644.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  645.     sprintf(relative, "%s/..", current);
  646.     HTStartAnchor(target, "", relative);
  647.     free(relative);
  648.  
  649.     PUTS("Up to ");
  650.     if (parent) {
  651.       char * printable = NULL;
  652.       StrAllocCopy(printable, parent + 1);
  653.       HTUnEscape(printable);
  654.       PUTS(printable);
  655.       free(printable);
  656.     } else {
  657.       PUTS("/");
  658.     }
  659.  
  660.     END(HTML_A);
  661.  
  662.     }
  663.     free(path);
  664. }
  665.         
  666.  
  667.  
  668. /*    Load a document
  669. **    ---------------
  670. **
  671. ** On entry,
  672. **    addr        must point to the fully qualified hypertext reference.
  673. **            This is the physsical address of the file
  674. **
  675. ** On exit,
  676. **    returns        <0        Error has occured.
  677. **            HTLOADED    OK 
  678. **
  679. */
  680. PUBLIC int HTLoadFile ARGS4 (
  681.     CONST char *,        addr,
  682.     HTParentAnchor *,    anchor,
  683.     HTFormat,        format_out,
  684.     HTStream *,        sink
  685. )
  686. {
  687.     char * filename;
  688.     HTFormat format;
  689.     char * nodename = 0;
  690.     char * newname=0;    /* Simplified name of file */
  691.     HTAtom * encoding;    /* @@ not used yet */
  692.     
  693. /*    Reduce the filename to a basic form (hopefully unique!)
  694. */
  695.     StrAllocCopy(newname, addr);
  696.     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
  697.     nodename=HTParse(newname, "", PARSE_HOST);
  698.     free(newname);
  699.     
  700.     format = HTFileFormat(filename, &encoding);
  701.  
  702.  
  703. #ifdef VMS
  704. /* Assume that the file is in Unix-style syntax if it contains a '/'
  705.    after the leading one @@ */
  706.     {
  707.         FILE * fp;
  708.     char * vmsname = strchr(filename + 1, '/') ?
  709.       HTVMS_name(nodename, filename) : filename + 1;
  710.     fp = fopen(vmsname, "r", "shr=put", "shr=upd");
  711.     
  712. /*    If the file wasn't VMS syntax, then perhaps it is ultrix
  713. */
  714.     if (!fp) {
  715.         char ultrixname[INFINITY];
  716.         if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
  717.         sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
  718.           fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
  719.         if (!fp) {
  720.         if (TRACE) fprintf(stderr, 
  721.                    "HTFile: Can't open as %s\n", ultrixname);
  722.         }
  723.     }
  724.         if (fp)
  725.         {
  726.         if (HTEditable(vmsname)) {
  727.         HTAtom * put = HTAtom_for("PUT");
  728.         HTList * methods = HTAnchor_methods(anchor);
  729.         if (HTList_indexOf(methods, put) == (-1)) {
  730.                HTList_addObject(methods, put);
  731.             }
  732.         }
  733.         HTParseFile(format, format_out, anchor, fp, sink);
  734.         fclose(fp);
  735.             return HT_LOADED;
  736.         }  /* If successfull open */
  737.     }
  738. #else
  739.  
  740.     free(filename);
  741.     
  742. /*    For unix, we try to translate the name into the name of a transparently
  743. **    mounted file.
  744. **
  745. **    Not allowed in secure (HTClienntHost) situations TBL 921019
  746. */
  747. #ifndef NO_UNIX_IO
  748.     /*  Need protection here for telnet server but not httpd server */
  749.      
  750.     if (!HTSecure) {        /* try local file system */
  751.     char * localname = HTLocalName(addr);
  752.     struct stat dir_info;
  753.     
  754. #ifdef GOT_READ_DIR
  755.  
  756. /*              Multiformat handling
  757. **
  758. **    If needed, scan directory to find a good file.
  759. **  Bug:  we don't stat the file to find the length
  760. */
  761.     if ( (strlen(localname) > strlen(MULTI_SUFFIX))
  762.        && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
  763.               MULTI_SUFFIX))) {
  764.         DIR *dp;
  765.  
  766.         STRUCT_DIRENT * dirbuf;
  767.         float best = NO_VALUE_FOUND;    /* So far best is bad */
  768.         HTFormat best_rep = NULL;    /* Set when rep found */
  769.         STRUCT_DIRENT best_dirbuf;    /* Best dir entry so far */
  770.  
  771.         char * base = strrchr(localname, '/');
  772.         int baselen;
  773.  
  774.         if (!base || base == localname) goto forget_multi;
  775.         *base++ = 0;        /* Just got directory name */
  776.         baselen = strlen(base)- strlen(MULTI_SUFFIX);
  777.         base[baselen] = 0;    /* Chop off suffix */
  778.  
  779.         dp = opendir(localname);
  780.         if (!dp) {
  781. forget_multi:
  782.         free(localname);
  783.         return HTLoadError(sink, 500,
  784.             "Multiformat: directory scan failed.");
  785.         }
  786.         
  787.         while ((dirbuf = readdir(dp))!=0) {
  788.             /* while there are directory entries to be read */
  789.         if (dirbuf->d_ino == 0) continue;
  790.                 /* if the entry is not being used, skip it */
  791.         
  792.         if (dirbuf->d_namlen > baselen &&      /* Match? */
  793.             !strncmp(dirbuf->d_name, base, baselen)) {    
  794.             HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
  795.             float value = HTStackValue(rep, format_out,
  796.                             HTFileValue(dirbuf->d_name),
  797.                         0.0  /* @@@@@@ */);
  798.             if (value != NO_VALUE_FOUND) {
  799.             if (TRACE) fprintf(stderr,
  800.                 "HTFile: value of presenting %s is %f\n",
  801.                 HTAtom_name(rep), value);
  802.             if  (value > best) {
  803.                 best_rep = rep;
  804.                 best = value;
  805.                 best_dirbuf = *dirbuf;
  806.                }
  807.             }    /* if best so far */             
  808.          } /* if match */  
  809.             
  810.         } /* end while directory entries left to read */
  811.         closedir(dp);
  812.         
  813.         if (best_rep) {
  814.         format = best_rep;
  815.         base[-1] = '/';        /* Restore directory name */
  816.         base[0] = 0;
  817.         StrAllocCat(localname, best_dirbuf.d_name);
  818.         goto open_file;
  819.         
  820.         } else {             /* If not found suitable file */
  821.         free(localname);
  822.         return HTLoadError(sink, 403,    /* List formats? */
  823.            "Could not find suitable representation for transmission.");
  824.         }
  825.         /*NOTREACHED*/
  826.     } /* if multi suffix */
  827. /*
  828. **    Check to see if the 'localname' is in fact a directory.  If it is
  829. **    create a new hypertext object containing a list of files and 
  830. **    subdirectories contained in the directory.  All of these are links
  831. **      to the directories or files listed.
  832. **      NB This assumes the existance of a type 'STRUCT_DIRENT', which will
  833. **      hold the directory entry, and a type 'DIR' which is used to point to
  834. **      the current directory being read.
  835. */
  836.     
  837.     
  838.     if (stat(localname,&dir_info) == -1) {     /* get file information */
  839.                                    /* if can't read file information */
  840.         if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
  841.  
  842.     }  else {        /* Stat was OK */
  843.         
  844.  
  845.         if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
  846.         /* if localname is a directory */    
  847.  
  848.         HTStructured* target;        /* HTML object */
  849.         HTStructuredClass targetClass;
  850.  
  851.         DIR *dp;
  852.         STRUCT_DIRENT * dirbuf;
  853.         
  854.         char * logical;
  855.         char * tail;
  856.         
  857.         BOOL present[HTML_A_ATTRIBUTES];
  858.         
  859.         char * tmpfilename = NULL;
  860.         struct stat file_info;
  861.         
  862.         if (TRACE)
  863.             fprintf(stderr,"%s is a directory\n",localname);
  864.             
  865. /*    Check directory access.
  866. **    Selective access means only those directories containing a
  867. **    marker file can be browsed
  868. */
  869.         if (HTDirAccess == HT_DIR_FORBID) {
  870.             free(localname);
  871.             return HTLoadError(sink, 403,
  872.             "Directory browsing is not allowed.");
  873.         }
  874.  
  875.  
  876.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  877.             char * enable_file_name = 
  878.             malloc(strlen(localname)+ 1 +
  879.              strlen(HT_DIR_ENABLE_FILE) + 1);
  880.             strcpy(enable_file_name, localname);
  881.             strcat(enable_file_name, "/");
  882.             strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  883.             if (stat(enable_file_name, &file_info) != 0) {
  884.             free(localname);
  885.             return HTLoadError(sink, 403,
  886.             "Selective access is not enabled for this directory");
  887.             }
  888.         }
  889.  
  890.  
  891.         dp = opendir(localname);
  892.         if (!dp) {
  893.             free(localname);
  894.             return HTLoadError(sink, 403, "This directory is not readable.");
  895.         }
  896.  
  897.  
  898.  /*    Directory access is allowed and possible
  899.  */
  900.         logical = HTAnchor_address((HTAnchor*)anchor);
  901.         tail = strrchr(logical, '/') +1;    /* last part or "" */
  902.         
  903.         target = HTML_new(anchor, format_out, sink);
  904.         targetClass = *target->isa;    /* Copy routine entry points */
  905.             
  906.           { int i;
  907.             for(i=0; i<HTML_A_ATTRIBUTES; i++)
  908.                 present[i] = (i==HTML_A_HREF);
  909.         }
  910.         
  911.                 HTDirTitles(target, (HTAnchor *)anchor);
  912.  
  913.         if (HTDirReadme == HT_DIR_README_TOP)
  914.             do_readme(target, localname);
  915.         {
  916.             HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
  917.  
  918.             while ((dirbuf = readdir(dp))!=0)
  919.             {
  920.                 HTBTElement * dirname = NULL;
  921.  
  922.                 /* while there are directory entries to be read */
  923.                 if (dirbuf->d_ino == 0)
  924.                   /* if the entry is not being used, skip it */
  925.                 continue;
  926.             
  927.  
  928.                 /* if the current entry is parent directory */
  929.             if ((*(dirbuf->d_name)=='.') ||
  930.                 (*(dirbuf->d_name)==','))
  931.                 continue;    /* skip those files whose name begins
  932.                         with '.' or ',' */
  933.  
  934.             dirname = (HTBTElement *)malloc(
  935.                     strlen(dirbuf->d_name) + 2);
  936.             if (dirname == NULL) outofmem(__FILE__,"DirRead");
  937.             StrAllocCopy(tmpfilename,localname);
  938.             if (strcmp(localname,"/"))
  939.  
  940.                     /* if filename is not root directory */
  941.                 StrAllocCat(tmpfilename,"/"); 
  942.  
  943.  
  944.             StrAllocCat(tmpfilename,dirbuf->d_name);
  945.             stat(tmpfilename, &file_info);
  946.             if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
  947.                 sprintf((char *)dirname,"D%s",dirbuf->d_name);
  948.             else sprintf((char *)dirname,"F%s",dirbuf->d_name);
  949.                 /* D & F to have first directories, then files */
  950.             HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
  951.             }
  952.  
  953.             /*    Run through tree printing out in order
  954.              */
  955.             {
  956.                 HTBTElement * next_element = HTBTree_next(bt,NULL);
  957.                 /* pick up the first element of the list */
  958.             char state;
  959.                 /* I for initial (.. file),
  960.                    D for directory file,
  961.                    F for file */
  962.             
  963.             state = 'I';
  964.  
  965.             while (next_element != NULL)
  966.                 {
  967.                 StrAllocCopy(tmpfilename,localname);
  968.                 if (strcmp(localname,"/")) 
  969.  
  970.                     /* if filename is not root directory */
  971.                     StrAllocCat(tmpfilename,"/"); 
  972.  
  973.                 StrAllocCat(tmpfilename,
  974.                     (char *)HTBTree_object(next_element)+1);
  975.                 /* append the current entry's filename to the path */
  976.                 HTSimplify(tmpfilename);
  977.                 /* Output the directory entry */
  978.                 if (strcmp((char *)
  979.                          (HTBTree_object(next_element)),"D.."))
  980.                 {                
  981.                 if (state != *(char *)(HTBTree_object(next_element)))
  982.                 {
  983.                     if (state == 'D')
  984.                         END(HTML_DIR);
  985.                     state = *(char *)
  986.                         (HTBTree_object(next_element))=='D'?'D':'F';
  987. #ifdef MSDOSMEM
  988.     {
  989.         extern void *vp_msdosmem;
  990.         extern void **vpp_msdosmem;
  991.         vp_msdosmem = NULL;
  992.         vpp_msdosmem = NULL;
  993.     }
  994. #endif /* MSDOSMEM */
  995.                     START(HTML_H2);
  996.                     PUTS(state == 'D'?"Subdirectories:":"Files");
  997.                     END(HTML_H2);
  998. #ifdef MSDOSMEM
  999.     {
  1000.         extern void *vp_msdosmem;
  1001.         extern void **vpp_msdosmem;
  1002.         vp_msdosmem = NULL;
  1003.         vpp_msdosmem = NULL;
  1004.     }
  1005. #endif /* MSDOSMEM */
  1006.                     START(HTML_DIR);
  1007.                 }
  1008. #ifdef MSDOSMEM
  1009.     {
  1010.         extern void *vp_msdosmem;
  1011.         extern void **vpp_msdosmem;
  1012.         vp_msdosmem = NULL;
  1013.         vpp_msdosmem = NULL;
  1014.     }
  1015. #endif /* MSDOSMEM */
  1016.                 START(HTML_LI);
  1017.                 }
  1018.                 HTDirEntry(target, tail,
  1019.                        (char*)HTBTree_object(next_element) +1);
  1020.  
  1021.                 next_element = HTBTree_next(bt,next_element);
  1022.                     /* pick up the next element of the list; 
  1023.                  if none, return NULL*/
  1024.             }
  1025.             if (state == 'I')
  1026.             {
  1027. #ifdef MSDOSMEM
  1028.     {
  1029.         extern void *vp_msdosmem;
  1030.         extern void **vpp_msdosmem;
  1031.         vp_msdosmem = NULL;
  1032.         vpp_msdosmem = NULL;
  1033.     }
  1034. #endif /* MSDOSMEM */
  1035.                 START(HTML_P);
  1036.                 PUTS("Empty Directory");
  1037.             }
  1038.             else
  1039.                 END(HTML_DIR);
  1040.             }
  1041.  
  1042.                 /* end while directory entries left to read */
  1043.             closedir(dp);
  1044.             free(logical);
  1045.             free(tmpfilename);
  1046.             HTBTreeAndObject_free(bt);
  1047.  
  1048.             if (HTDirReadme == HT_DIR_README_BOTTOM)
  1049.               do_readme(target, localname);
  1050.             FREE_TARGET;
  1051.             free(localname);
  1052.             return HT_LOADED;    /* document loaded */
  1053.         }
  1054.  
  1055.         } /* end if localname is directory */
  1056.     
  1057.     } /* end if file stat worked */
  1058.     
  1059. /* End of directory reading section
  1060. */
  1061. #endif
  1062. open_file:
  1063.     {
  1064.         FILE * fp = fopen(localname,"r");
  1065.  
  1066.         if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
  1067.                 localname, (void*)fp);
  1068.         if (fp) {        /* Good! */
  1069.         if (HTEditable(localname)) {
  1070.             HTAtom * put = HTAtom_for("PUT");
  1071.             HTList * methods = HTAnchor_methods(anchor);
  1072.             if (HTList_indexOf(methods, put) == (-1)) {
  1073.             HTList_addObject(methods, put);
  1074.             }
  1075.         }
  1076.         free(localname);
  1077.         HTParseFile(format, format_out, anchor, fp, sink);
  1078.         fclose(fp);
  1079.         return HT_LOADED;
  1080.         }  /* If succesfull open */
  1081.     }    /* scope of fp */
  1082.     }  /* local unix file system */    
  1083. #endif
  1084. #endif
  1085.  
  1086. #ifndef DECNET
  1087. /*    Now, as transparently mounted access has failed, we try FTP.
  1088. */
  1089.     {
  1090.     if (strcmp(nodename, HTHostName())!=0)
  1091.         return HTFTPLoad(addr, anchor, format_out, sink);
  1092.     }
  1093. #endif
  1094.  
  1095. /*    All attempts have failed.
  1096. */
  1097.     {
  1098.         if (TRACE)
  1099.         printf("Can't open `%s', errno=%d\n", addr, errno);
  1100.  
  1101.     return HTLoadError(sink, 403, "Can't access requested file.");
  1102.     }
  1103.     
  1104.  
  1105. }
  1106.  
  1107. /*        Protocol descriptors
  1108. */
  1109. GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
  1110. GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };